Skip to content

feat(examples): generate the catalogue one way, from an empty tree - #484

Merged
DemchaAV merged 2 commits into
developfrom
feat/canonical-example-generation
Aug 1, 2026
Merged

feat(examples): generate the catalogue one way, from an empty tree#484
DemchaAV merged 2 commits into
developfrom
feat/canonical-example-generation

Conversation

@DemchaAV

@DemchaAV DemchaAV commented Aug 1, 2026

Copy link
Copy Markdown
Owner

First of two for #461. This one makes the catalogue a statement about the code; the next wires it into cut-release.ps1 and adds the CI diff check. Nothing here touches the release script, CI, the root README or CodeQL.

Why

The runner only ever wrote. Rename an example's output or delete the example, and its old document stayed in target/generated-pdfs — copied to the site, counted by the guards that read the tree, and indistinguishable from a document the current code produces. The tree recorded the order in which commands had been run rather than what the code does.

Measured on develop: five example classes were never wired into the runner, and ten committed previews had no generated source. They are the same story from two ends.

  • Seven of the ten are pre-2.0 renders of CV presets. The runner already produces each of them under its current name, so every gallery row linked the older snapshot of a preset that had a newer one sitting beside it. CvFileExample and CvModernV2Example turn out to render the identical document — same factory, same fixture — which is why one of them was never wired in.
  • Two, an invoice and a proposal, were left behind by a rename. Nothing in the repository links them.
  • One, the cover letter, backed a gallery link with a file no code produced, because CoverLetterFileExample was not in the runner.

What

One canonical path. ExampleOutputPaths.clean() empties the tree, and GenerateAllExamples calls it before writing. The clean step lives in the runner because the release script and CI invoke the runner directly — putting it anywhere else would cover only the path that already remembered. GeneratedCatalogue stops keeping its own copy and delegates.

ShowcaseSync mirrors. It already cleared the published files; it now removes the directories too, so a category that stops producing anything leaves nothing behind on the deployed tree.

Two examples wired in. CoverLetterFileExample and CvMintEditorialCustomExample. Both have showcase entries; the cover letter's is registered directly rather than through the letter() helper, which points into the layered preset package — this example composes without a preset, and the source-link guard caught the mismatch.

Nine previews removed, seven replaced. The gallery rows follow the current render. The catalogue and the register now agree exactly: 93 documents, 93 entries, no placeholder cards, no entry without a document.

The two gallery examples stay unwired on purpose: they render one document per preset, 31 in all, duplicating the individual preset examples under their pre-2.0 names. Publishing them is a decision about what the showcase is for, not a gap.

Tests

examples suite: 52 tests, BUILD SUCCESS. qa gate: 688. Core guard job: 37.

Check Result
plant a stale document, run the runner removed by the runner itself
every register entry has a generated document 93/93
every generated document has a register entry 93/93 today, measured, not asserted — the guard covers the other direction on purpose, so the fallback card stays reachable
every codeUrl resolves to a file passes; caught the cover-letter path before it shipped
every preview link in the READMEs resolves 0 broken
a partial catalogue is refused delete two documents, sync names them and stops
the full catalogue publishes 101 documents synced, two directories Windows would not release logged and left

The runner only wrote. Rename an example's output or delete the example
and its old document stayed in target/, published to the site and counted
by the guards that read the tree, indistinguishable from a current one.
Generation now clears the tree first, and it does so inside the runner —
the release script and CI invoke that directly, so a clean step living
anywhere else would have covered only the path that already remembered.
The test helper stops keeping its own copy of the logic.

ShowcaseSync removes the directories it replaces, not only the files, so
a category that stops producing anything leaves nothing behind.

Two examples were never wired in. One of them, the cover letter, backed a
gallery link with a file no code produced; the other renders a preset
through a hand-built theme. Both generate now and both have a showcase
entry — the cover letter's registered directly, because the helper that
builds letter entries points into the layered preset package and this one
composes without a preset.

Nine committed previews go. Seven were pre-2.0 renders of CV presets the
runner already produces under their current names, so every gallery row
linked the older snapshot of a preset that had a newer one beside it;
those rows now follow the current render. The remaining two, an invoice
and a proposal, were left behind by a rename with nothing linking them.
…Windows

Clearing the tree before generation moved the risk somewhere else. A run
that dies partway now leaves exactly what it managed to write, and the
sync's only defence was "not empty" — so it would mirror the remains,
delete every published document the run never reached, and rewrite the
manifest to match. The site would come out internally consistent and
quietly missing half its examples. It refuses now, against the register:
that is the one list saying what the catalogue is meant to contain, and
deleting an example still shrinks it legitimately because the entry goes
with it.

Removing the published directories failed on Windows with
AccessDeniedException on web/showcase/pdf/templates/schedule — the exact
failure the comment I replaced had recorded, and CI could not see it
because CI is Linux. Files are still removed strictly; a directory the
filesystem will not release is logged and left, because an empty folder
nobody can reach through the manifest is not worth failing a publish for.

The clean now names the file it could not delete and says a viewer is
usually holding it, instead of surfacing a bare AccessDeniedException
from a run that has produced nothing yet.

The custom Mint Editorial card said the example builds a BrandTheme. It
sets one Options field; BrandTheme is a different overload on the same
preset, so the description pointed at the wrong one of two adjacent APIs.

Two gallery rows linked a preview produced by one example and credited
another as its source.
@DemchaAV
DemchaAV merged commit b27c559 into develop Aug 1, 2026
10 checks passed
@DemchaAV
DemchaAV deleted the feat/canonical-example-generation branch August 1, 2026 20:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant